<?php
/**
 * The Wikka mainscript: Command Line Interface (cli/cron) edition
 *
 * This script is designed to allow wikka to be run from the command line
 * as a non-webserver process, specifically for cron tasks.  It requires
 * the following:
 *
 *  - cli.config.php
 *  - handlers/page/cli.php
 *  - a cli handler (see handlers/page/cli_test.php) or action (see
 *    actions/clitest.php)
 *
 * USAGE
 *  test:               php WIKKA_BASEPATH/cli.php -t
 *  load page:          php WIKKA_BASEPATH/cli.php PAGE
 *  call method:        php WIKKA_BASEPATH/cli.php cli/METHOD
 *  with config key:    php WIKKA_BASEPATH/cli.php -c CONFIG_KEY PAGE/METHOD
 *
 * NOTES
 *      CONFIG_KEY is useful in the event you have multiple configuration keys
 *      in your config file.  For example, see http://bit.ly/cx5eND
 *
 *      Tested in php 5.2.4 and 4.4.2
 *
 * This file was originally written by Hendrik Mans for WakkaWiki
 * and released under the terms of the modified BSD license
 * @see /docs/WakkaWiki.LICENSE
 *
 * @package Wikka
 * @subpackage Core
 * @version $Id$
 * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
 * @see /docs/Wikka.LICENSE
 * @filesource
 *
 * @author	{@link http://www.mornography.de/ Hendrik Mans}
 * @author	{@link http://wikkawiki.org/JsnX Jason Tourtelotte}
 * @author	{@link http://wikkawiki.org/JavaWoman Marjolein Katsma}
 * @author	{@link http://wikkawiki.org/NilsLindenberg Nils Lindenberg}
 * @author	{@link http://wikkawiki.org/DotMG Mahefa Randimbisoa}
 * @author	{@link http://wikkawiki.org/DarTar Dario Taraborelli}
 * @author	{@link http://wikkawiki.org/BrianKoontz Brian Koontz}
 * @author	{@link http://wikkawiki.org/TormodHaugen Tormod Haugen}
 * @author	{@link http://klenwell.com/is Tom Atwell}
 *
 * @copyright Copyright 2002-2003, Hendrik Mans <hendrik@mans.de>
 * @copyright Copyright 2004-2005, Jason Tourtelotte <wikka-admin@jsnx.com>
 * @copyright Copyright 2006-2009, {@link http://wikkawiki.org/CreditsPage Wikka Development Team}
 * @copyright Copyright 2010       Tom Atwell <klenwell@gmail.com>
 *
 */


/**
 * Import Main Libraries
 * @see libs/Wakka.class.php
 */
$doc_root = dirname(__FILE__) . '/';
$inc_path = sprintf('%s:%s:', ini_get('include_path'), $doc_root);
ini_set('include_path', $inc_path);

# must chdir to load dirs correctly
if ( ! chdir($doc_root) ) {
    die("unable to chdir to $doc_root");
}

if ( file_exists('libs/Wakka.class.php')) {
    require_once('libs/Compatibility.lib.php');
    require_once('libs/Wakka.class.php');
    require_once('3rdparty/core/php-gettext/gettext.inc');
}
else {
    die(ERROR_WAKKA_LIBRARY_MISSING);
}


/**
 * WikkaCli Class (extends Wakka class)
 * @see libs/Wakka.class.php
 */
class WikkaCli extends Wakka {

    # override this (we don't need all the big evil nastiness)
    function Run($tag, $method='')
    {
        if ( empty($method) ) {
            $this->method = 'cli';
        }
        else {
            $this->method = $method;
        }
        if (!$this->tag = trim($tag)) {
            $this->Redirect($this->Href("", $this->config["root_page"]));
        }

        # load page
        $this->SetPage($this->LoadPage($tag, (isset($_GET['time']) ? $_GET['time'] :''))); #312
        $this->ACLs = $this->LoadAllACLs($this->tag);

        header("Content-type: text/plain");
        print $this->GetHandler($this->method);
    }
}

/**
 * RequestHandler class
 * @this runs the script by reorganizing most the wakka.php code into a more orderly structure
 */
class RequestHandler {

    var $start_time = 0;
    var $pretty_print = 1;
    var $debug = 0;

    function RequestHandler() {
        $this->start_time = getmicrotime();
        $this->argv = $_SERVER['argv'];
    }

    function security_checks() {
        $this->cli_check();
        $this->sanity_checks();
        $this->fix_magic_quotes();
    }

    function cli_check() {
        if ( ! php_sapi_name() == 'cli' ) {
            trigger_error('This script is accessible only from the PHP CLI.',
                          E_USER_ERROR);
        }
        define('WIKKA_ALLOW_CLI', 1);
    }

    function sanity_checks() {
        # minimum version check
        if ( !function_exists('version_compare') ||
                version_compare(phpversion(),MINIMUM_PHP_VERSION,'<') )	 {
            $php_version_error = sprintf(ERROR_WRONG_PHP_VERSION,MINIMUM_PHP_VERSION);
            trigger_error($php_version_error, E_USER_ERROR);
        }

        # mysql check
        if (!function_exists('mysql_connect')) {
            trigger_error(ERROR_MYSQL_SUPPORT_MISSING, E_USER_ERROR);
        }
    }

    function fix_magic_quotes() {
        set_magic_quotes_runtime(0);
        if (get_magic_quotes_gpc())
        {
            magicQuotesWorkaround($_POST);
            magicQuotesWorkaround($_GET);
            magicQuotesWorkaround($_COOKIE);
        }
    }

    function parse_wakka_url() {
        if ( !preg_match('@wakka=@',$_SERVER['REQUEST_URI'])
                && isset($_SERVER['QUERY_STRING'])
                && preg_match('@wakka=@',$_SERVER['QUERY_STRING'])) {

            # remove 'wikka.php' and request (page name) from 'request' part: should not be part of base_url!
            $query_part = preg_replace('@wakka=@', '', $_SERVER['QUERY_STRING']);
            $t_request  = preg_replace('@'.preg_quote('wikka.php').'@', '', $t_request);
            $t_request  = preg_replace('@'.preg_quote($query_part).'@', '', $t_request);
            $t_query = '';
            $t_rewrite_mode = 1;
        }
        else {
            # no rewritten request apparent
            $t_query = '?wakka=';
            $t_rewrite_mode = 0;
        }
    }

    function load_wikka_config() {
        $wakkaConfig = array();

        # upgrade from wakka (is this still relevant?)
        if ( file_exists('wakka.config.php')) {
            rename('wakka.config.php', 'wikka.config.php"');
        }

        # use a define instead of GetEnv [SEC]
        if (defined('WAKKA_CONFIG')) {
            $configfile = WAKKA_CONFIG;
        }
        else {
            $configfile = 'wikka.config.php';
        }

        #if (file_exists($configfile)) include($configfile);
        require_once($configfile);
        require_once('cli.config.php');
        $wakkaConfigLocation = $configfile;

        # remove obsolete config settings (should come before merge!)
        # TODO move these checks to a directive file to be used by the installer/upgrader, #97
        # since 1.1.6.4
        if (isset($wakkaConfig['header_action'])) {
            unset($wakkaConfig['header_action']);
        }
        if (isset($wakkaConfig['footer_action'])) {
            unset($wakkaConfig['footer_action']);
        }

        # Remove old stylesheet, #6 (since 1.2)
        if(isset($wakkaConfig['stylesheet'])) {
            unset($wakkaConfig['stylesheet']);
        }

        # Add plugin paths if they do not already exist
        if( isset($wakkaConfig['action_path'])
                && preg_match('/plugins\/actions/', $wakkaConfig['action_path']) <= 0) {
            $wakkaConfig['action_path'] = "plugins/actions," .  $wakkaConfig['action_path'];
        }
        if ( isset($wakkaConfig['handler_path'])
                && preg_match('/plugins\/handlers/', $wakkaConfig['handler_path']) <= 0 ) {
            $wakkaConfig['handler_path'] = "plugins/handlers," .  $wakkaConfig['handler_path'];
        }
        if ( isset($wakkaConfig['wikka_template_path'])
                && preg_match('/plugins\/templates/', $wakkaConfig['wikka_template_path']) <= 0 ) {
            $wakkaConfig['wikka_template_path'] = "plugins/templates," .  $wakkaConfig['wikka_template_path'];
        }
        if ( isset($wakkaConfig['wikka_formatter_path'])
                && preg_match('/plugins\/formatters/', $wakkaConfig['wikka_formatter_path']) <= 0 ) {
            $wakkaConfig['wikka_formatter_path'] = "plugins/formatters," .  $wakkaConfig['wikka_formatter_path'];
        }

        $wakkaConfig = array_merge($wakkaDefaultConfig, $wakkaConfig);	// merge defaults with config from file

        # set some values to properties
        $this->wikka_version = $wakkaConfig['wakka_version'];

        # version check
        $this->wakka_version_check($wakkaConfig);
        $this->check_for_lock($wakkaConfig);

        return $wakkaConfig;
    }

    function wakka_version_check($wakkaConfig) {
        if ( !isset($wakkaConfig['wakka_version'])) {
            $wakkaConfig['wakka_version'] = 0;
        }

        if ( $wakkaConfig['wakka_version'] !== WAKKA_VERSION ) {
            $f = <<<HEREDOC

VERSION CHECK ERROR

WAKKA_VERSION (cli.config.php)      %s
wakka_version (wikka.config.php)    %s

These values must match. Please update the files.

HEREDOC;
            die(sprintf($f, WAKKA_VERSION, $wakkaConfig['wakka_version']));
        }
    }

    function check_for_lock($wakkaConfig) {
        if ( file_exists('locked') )
        {
            # read password from lockfile
            $lines = file("locked");
            $lockpw = trim($lines[0]);

            # is authentification given?
            if ( isset($_SERVER["PHP_AUTH_USER"]) ) {
                if ( ! (($_SERVER["PHP_AUTH_USER"] == "admin")
                        && ($_SERVER["PHP_AUTH_PW"] == $lockpw)) ) {
                    $ask = 1;
                }
            }
            else {
                $ask = 1;
            }

            if ( $ask ) {
                header( sprintf('WWW-Authenticate: Basic realm="%s Install/Upgrade Interface"',
                                $wakkaConfig["wakka_name"]) );
                header("HTTP/1.0 401 Unauthorized");
                print WIKI_UPGRADE_NOTICE;
                exit;
            }
        }
    }

    function start_session($wakkaConfig) {
        $this->base_url_path = preg_replace('/wikka\.php/', '', $_SERVER['SCRIPT_NAME']);
        $wikka_cookie_path = ('/' == $this->base_url_path)
            ? '/'
            : substr($this->base_url_path,0,-1);
        session_set_cookie_params(0, $wikka_cookie_path);
        session_name(md5(BASIC_COOKIE_NAME.$wakkaConfig['wiki_suffix']));
        session_start();
    }

    function get_page_and_method($wakka) {
        # Remove leading slash.
        $page = $method = NULL;
        $wakka = preg_replace("/^\//", "", $wakka);

        if ( preg_match("#^(.+?)/(.*)$#", $wakka, $matches) ) {
            list(, $page, $method) = $matches;
        }
        else if (preg_match("#^(.*)$#", $wakka, $matches)) {
            list(, $page) = $matches;
        }

        //Fix lowercase mod_rewrite bug: URL rewriting makes pagename lowercase. #135
        if ((strtolower($page) == $page) && (isset($_SERVER['REQUEST_URI']))) #38
        {
            $pattern = preg_quote($page, '/');
            if ( preg_match( "/($pattern)/i",
                             urldecode($_SERVER['REQUEST_URI']),
                             $match_url ) ) {
                $page = $match_url[1];
            }
        }

        return array($page, $method);
    }

    function instantiate_wakka_object($wakkaConfig) {

        # dependant wikka libraries require a global here
        global $wakka;

        $wakka = instantiate('WikkaCli',$wakkaConfig);

        if ( ! $wakka->dblink ) {
            trigger_error(
                'Unable to connect to db: check config file settings; use -c to set key',
                E_USER_ERROR );
        }

        return $wakka;
    }

    function parse_wikka_request() {
        $PageMethodTuple = $this->get_page_and_method($this->wikka_uri);
        $this->page = $PageMethodTuple[0];
        $this->method = $PageMethodTuple[1];
        return $PageMethodTuple;
    }

    function run_wikka_engine($wakka, $page, $method) {

        if ( !isset($method) || is_null($method) ) {
            $method='cli';
        }
        $wakka->config['enable_user_host_lookup'] = 0;

        # This method used to call: $wakka->Run($page, $method);
        # That no longer works. These three lines extract the essential parts
        # of the Run method:
        $wakka->SetPage($wakka->LoadPage($page, $wakka->GetSafeVar('time', 'get')));
        $wakka->handler = 'cli';
        print($wakka->handler($wakka->handler));

        # This stops the buffer and collects the output
        $content = ob_get_clean();
        $page_output = $content;
        return $page_output;
    }

    function run_wikka($wakkaConfig, $page, $method) {
        $wakka = $this->instantiate_wakka_object($wakkaConfig);
        $page_output = $this->run_wikka_engine($wakka, $page, $method);
        return $page_output;
    }

    function parse_command_line()
    {
        $ShortOpts = array(
            'c:',   # config key ($_SERVER['SERVER_NAME'])
            't',    # test
        );

        $options = getopt(implode('',$ShortOpts));

        if ( isset($options['c']) ) {
            $_SERVER['SERVER_NAME'] = $options['c'];
        }

        if ( isset($options['t']) ) {
            $run_mode = 'test';
        }
        else {
            $run_mode = 'default';
        }

        $this->wikka_uri = array_pop($_SERVER['argv']);
        $this->basename = basename($_SERVER['argv'][0]);

        return $run_mode;
    }


    # output methods
    function output($content) {
        if ( $this->debug ) {
            $this->debug_output();
        }

        if ( $this->pretty_print ) {
            $this->pretty_print_output($content);
        }
        else {
            print $content;
        }

    }

    function debug_output() {
        $format_ = <<<HDOC
wikka cron script
-----------------
basename:       %s
wikka version:  %s
wikka page:     %s
wikka method:   %s

argv:
%s

_SERVER:
%s

USAGE:
    php %s [-c CONFIG_KEY] page/method

script complete in %.4f s

HDOC;
        printf( $format_,
                $this->basename,
                $this->wikka_version,
                $this->page,
                $this->method,
                print_r($this->argv,1),
                print_r($_SERVER,1),
                __FILE__,
                getmicrotime() - $this->start_time );
    }

    function pretty_print_output($content) {
        $content = preg_replace('%<br[^>]*>|\n%', '***', $content);
        $content = preg_replace('%\s+%', ' ', $content);
        $content = str_replace('***', "\n", $content);
        $content = preg_replace('%\n{3,}%', "\n\n", $content);
        print $content;
    }


    # main methods
    function main_wikka() {
        ob_start();
        $this->security_checks();
        $wakkaConfig = $this->load_wikka_config();
        list($page, $method) = $this->parse_wikka_request();
        $wikka_output = $this->run_wikka($wakkaConfig, $page, $method);
        ob_end_clean();
        return $wikka_output;
    }

    function main_test() {
        ob_start();
        $wakkaConfig = $this->load_wikka_config();
        $this->security_checks();
        ob_end_clean();
        $this->debug_output();
    }

    function main()
    {
        $run_mode = $this->parse_command_line();

        if ( $run_mode == 'test' ) {
            $this->main_test();
        }
        else {
            $content = $this->main_wikka();
            $this->output($content);
        }
    }
}


# Main
$CliHandler = new RequestHandler();
$CliHandler->main();

?>
